:-dynamic( nb_E/1 ).
:-dynamic( nb_R/1 ).
:-dynamic( plateau/1 ).
:-dynamic( player/1 ).
:-dynamic( vainqueur/1 ).

vainqueur(0).
nb_E(0).
nb_R(0).

% Stocke le plateau dans la base de faits
% Ensuite on fait juste plateau(T) pour le récupérer dans T
% Idem pour player
set_plateau(P) :- retractall(plateau(_)),asserta(plateau(P)).
set_player(P) :- retractall(player(_)),asserta(player(P)).

ajouter_E(Arrivee, Orientation, Plateau):- nb_E(X), X < 5, retract(nb_E(X)) , X1 is X + 1, asserta(nb_E(X1)), %on ajoute et on stocke le nb d'éléphant en jeu
							write('Nombre d elephants en jeu = '), write(X1),% affichage du nombre d'éléphant en jeu au joueur
							nth0(0, Plateau, Elephants),% Stockage dans la liste Elephant de la partie elephant du plateau
							case_dispoListe((0,0), Elephants, N), %On cherche la partie de liste disponible pour stcoker la position du npuvel elephant : N
							Case = (Arrivee,Orientation),%on formalise la case à écrire(position+orientation)
							modif_liste(Case, Elephants, N,Q), %modification de la liste Elephant : nouvelle liste dans Q
							modif_liste(Q, Plateau, 0, F), %on recrée le plateau
							set_plateau(F),!.	%on sauvegarde	
ajouter_E(_,_,_):- write('Plus de piece disponible'), fail.

ajouter_R(Arrivee, Orientation, Plateau):- nb_R(X), X < 5, retract(nb_R(X)) , X1 is X + 1, asserta(nb_R(X1)), %on ajoute et on stocke le nb de rhino en jeu
							write('Nombre de rhinoceros en jeu = '), write(X1),% affichage du nombre de rhino en jeu au joueur
							nth0(1, Plateau, Rhino),% Stockage dans la liste Rhino de la partie rhonoceros du plateau
							case_dispoListe((0,0), Rhino, N), %On cherche la partie de liste disponible pour stcoker la position du nouveau rhino : N
							Case = (Arrivee,Orientation),%on formalise la case à écrire(position+orientation)
							modif_liste(Case, Rhino, N,Q), %modification de la liste Rhino : nouvelle liste dans K
							modif_liste(Q, Plateau, 1, F),%on recrée le plateau
							set_plateau(F),!.%on sauvegarde	
ajouter_R(_,_,_):- write('Plus de piece disponible'), fail.

%Modifier le nième lement d'une liste : Elem = nouvelle donnée, Liste, Position de l'élément, et nouvelle liste obtenue
modif_liste(Elem,[_|Q],0,[Elem|Q]). 
modif_liste(Elem,[T|Q],Position,[T|Ys]) :- Position > 0, K1 is Position - 1, modif_liste(Elem,Q,K1,Ys). 

%retourne la position d'un élément X dans une liste, Utilisé pour avoir la place d'une case dispo pour le plateau 
case_dispoListe(X,[X|_],0).
case_dispoListe(X,[_|Q],N):-  case_dispoListe(X,Q,N1), N is N1 + 1.
%retirer une piece
retirer_P(NumCase,_):-contenu_case(NumCase, e, _),nb_E(X), X>0, retract(nb_E(X)), X1 is X - 1, asserta(nb_E(X1)),write('Nombre d elephants en jeu = '), write(X1),!.
retirer_P(NumCase,_):-contenu_case(NumCase, r, _),nb_R(X), X>0, retract(nb_R(X)), X1 is X - 1, asserta(nb_R(X1)),write('Nombre de rhinoceros en jeu = '), write(X1),!.
retirer_P(NumCase,Orientation):-contenu_case(NumCase, m, _),trouve_gagnant(NumCase, Orientation, Gagnant), retract(vainqueur(0)), asserta(vainqueur(Gagnant)).

% Trouve le gagnant lorsqu une montagne a été sortie
trouve_gagnant(NumCase, Orientation, Gagnant):-oppose(Orientation, DirOp),next_case(NumCase, DirOp, NextCase),write(NextCase),contenu_case(NextCase, Gagnant, Orientation),write(Gagnant),!.
trouve_gagnant(NumCase, Orientation, Gagnant):-oppose(Orientation, DirOp),next_case(NumCase, DirOp, NextCase),trouve_gagnant(NextCase, Orientation, Gagnant).

plateau_depart([[(0,0),(0,0),(0,0),(0,0),(0,0)],[(0,0),(0,0),(0,0),(0,0),(0,0)],[32,33,34]]). % Plateau de depart Pas de pièce presentes

% Recherche d un élement dans une liste
element(X, [X|_]). 
element(X, [_|R]) :- element(X,R).

:-include('affichage.pl').

% Obtenir le numéro ou la ligne/colonne d une case
caseLC(X, L, C):-integer(L), integer(C), X is L*10+C.
caseLC(X, L, C):-integer(X), L is X//10, C is X mod 10.

% Format d un Coup : (Départ, Arrivée, OrientationArrivée), avec Départ=0 si entrée sur le plateau, Arrivée=0 si sortie
% Si case libre > ok, si case prise > essaie de pousser
inserer_P(Coup, e):- Coup=(Depart,Arrivee,Orientation),Depart=0, bord(Arrivee), \+ place_disponible(Arrivee),plateau(P),ajouter_E(Arrivee, Orientation, P),!.
inserer_P(Coup, e):- Coup=(Depart,Arrivee,Orientation),Depart=0, bord(Arrivee), decal_ligne(Arrivee, Orientation), plateau(P),ajouter_E(Arrivee, Orientation, P).
inserer_P(Coup, r):- Coup=(Depart,Arrivee,Orientation),Depart=0, bord(Arrivee), \+ place_disponible(Arrivee),plateau(P),ajouter_R(Arrivee, Orientation, P).
inserer_P(Coup, r):- Coup=(Depart,Arrivee,Orientation),Depart=0, bord(Arrivee), decal_ligne(Arrivee, Orientation),  plateau(P),ajouter_R(Arrivee, Orientation, P).
inserer_P(_, _):-fail.

% Récupérer le contenu d une case (orientation peut être spécifié ou demandé)
contenu_case(Case, e, Orientation) :- plateau(T), X=(Case, Orientation),nth0(0, T, Elephants),element(X,Elephants),!.
contenu_case(Case, r, Orientation) :- plateau(T), X=(Case, Orientation),nth0(1, T, Rhinoceros),element(X,Rhinoceros),!.
contenu_case(Case, m, _) :- plateau(T), X=Case,nth0(2, T, Montagnes),element(X,Montagnes),!.

%recupérer la liste des elements de même type qu'une case donnée avec le type et sa place
type_case(Case,Plateau, L, Type,N,Orientation):-nth0(0, Plateau, L),case_dispoListe((Case,Orientation),L, N),Type =0,!.
type_case(Case,Plateau, L, Type,N,Orientation):-nth0(1, Plateau, L),case_dispoListe((Case,Orientation),L, N), Type=1,!.
type_case(Case,Plateau, L, Type,N,_):-nth0(2, Plateau, L),case_dispoListe(Case,L, N), Type=2,!.

% Définit les directions opposées
oppose(n,s).
oppose(w,e).
oppose(e,w).
oppose(s,n).

% Gestion de la pousée
% On fait la somme de toute la ligne de : 
% - force = force que je peux pousser (pieces à moi dans la bonne orientation)
% - masse = masse à pousser (pions de l adversaire orientés face à moi et montagnes)
% Poussée possible si sur toute la chaine force > 0 et force ≥ masse (controler a chaque etape)
% appeler sur poussee la case d arrivee avec force=1
poussee_possible(CaseDest, Direction):-poussee(CaseDest, Direction, 1, 0),!.
poussee(Case, Direction, Force, Masse):-contenu_case(Case, _, Direction),NForce is Force + 1,!,next_case(Case, Direction, NCase),poussee(NCase, Direction, NForce, Masse).
poussee(Case, Direction, Force, Masse):-contenu_case(Case, _, Orientation),oppose(Orientation,Direction),NMasse is Masse + 1,!,next_case(Case, Direction, NCase),poussee(NCase, Direction, Force, NMasse).
poussee(Case, Direction, Force, Masse):-contenu_case(Case, m, _),NMasse is Masse + 1,!,next_case(Case, Direction, NCase),poussee(NCase, Direction, Force, NMasse).
poussee(_, _, Force, Masse):-Force>0,Force>Masse,!.

% Calcule le numero de la prochaine case
next_case(Case, n, NCase):-caseLC(Case, L, C),L1 is L + 1,L1 < 6,caseLC(NCase, L1, C).
next_case(Case, s, NCase):-caseLC(Case, L, C),L1 is L - 1,L1 > 0,caseLC(NCase, L1, C).
next_case(Case, w, NCase):-caseLC(Case, L, C),C1 is C - 1,C1 > 0,caseLC(NCase, L, C1).
next_case(Case, e, NCase):-caseLC(Case, L, C),C1 is C + 1,C1 < 6,caseLC(NCase, L, C1).

%changement de l'element Place d'une Liste avec le nouveau Numero NumCaseA
decal_element(NumCaseA,Orientation, Liste, Type, Place):-Type <2,%cas de elephants ou rhino
											plateau(Plat),
											modif_liste((NumCaseA,Orientation), Liste, Place, NewL),%on modifie
											modif_liste(NewL, Plat, Type, F), %on recrée le plateau
											set_plateau(F),!.%on sauvegarde
											
decal_element(NumCaseA,_, Liste, Type, Place):-Type = 2,%cas des montagnes
											plateau(Plat),
											modif_liste(NumCaseA, Liste, Place, NewL),%on modifie
											modif_liste(NewL, Plat, Type, F), % on recrée le plateau
											set_plateau(F),!. % on sauvegarde
											
% Décale toute une rangée de piéces d'une ligne à partir d'une case vers la direction donnée
decal_ligne(NumCase,_):- \+ place_disponible(NumCase). % On regarde si la case est vide
decal_ligne(NumCase,n):-  NumCase >45,%on sort du plateau
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,_),%on recupére tout les données
											retirer_P(Type,n),
											decal_element(0, 0,L, Type,P).
decal_ligne(NumCase, n):- NewNumCase is NumCase + 10, % On pousse vers le nord
											decal_ligne(NewNumCase, _),%on rappel la fonction jusqu'a un emplacement vide
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,OrP),%on recupére tout les données
											decal_element(NewNumCase, OrP,L, Type,P).%on decal l'élément
% pousse Sud
decal_ligne(NumCase,s):- NumCase <21,%on sort du plateau
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,_),%on recupére tout les données
											retirer_P(Type,s),
											decal_element(0, 0,L, Type,P).
decal_ligne(NumCase, s):- NewNumCase is NumCase - 10, %case au sud
											decal_ligne(NewNumCase, _),%on rappel la fonction jusqu'a un emplacement vide
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,OrP),%on recupére tout les données
											decal_element(NewNumCase, OrP,L, Type,P).%on decal l'élément
											
% pousse est
decal_ligne(NumCase,e):- Z is NumCase mod 10, Z > 4,%on sort du plateau
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,_),%on recupére tout les données
											retirer_P(Type,e),
											decal_element(0, 0,L, Type,P).
decal_ligne(NumCase, e):- NewNumCase is NumCase + 1, %case a l'est
											decal_ligne(NewNumCase, _),%on rappel la fonction jusqu'a un emplacement vide
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,OrP),%on recupére tout les données
											decal_element(NewNumCase, OrP,L, Type,P).%on decal l'élément
											
% pousse ouest
decal_ligne(NumCase,w):- Z is NumCase mod 10, Z < 2,%on sort du plateau
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,_),%on recupére tout les données
											retirer_P(Type,w),
											decal_element(0, 0,L, Type,P).
decal_ligne(NumCase, w):-NewNumCase is NumCase - 1, %case a l'ouest
											decal_ligne(NewNumCase, _),%on rappel la fonction jusqu'a un emplacement vide
											plateau(Plat),
											type_case(NumCase,Plat, L, Type, P,OrP),%on recupére tout les données
											decal_element(NewNumCase, OrP,L, Type,P).%on decal l'élément											
																
% test si la case est sur le bord du plateau
bord(X):- Z is X mod 10, Z ==1, !.
bord(X):- Z is X mod 10, Z ==5, !.
bord(X):- X > 11, X < 15, !.
bord(X):- X >51, X < 55, !.

deplacer_piece(Coup,Plateau):-Coup=(Depart,Arrivee,_),
										Arrivee==0,%si on sort une piece
										type_case(Depart, Plateau, L, Type, Place, _),%on recup les donnée Orp= orientation ancienne
										decal_element(0,0, L, Type, Place),%on deplace
										retirer_P(Type,_), !.
deplacer_piece(Coup,Plateau):-Coup=(Depart,Arrivee,Orientation),
										\+ place_disponible(Arrivee),
										type_case(Depart, Plateau, L, Type, Place, _),%on recup les donnée Orp= orientation ancienne
										decal_element(Arrivee,Orientation, L, Type, Place),!.%on deplace
										
deplacer_piece(Coup,Plateau):-Coup=(Depart,Arrivee,Orientation),
										Depart==Arrivee,
										type_case(Depart, Plateau, L, Type, Place, _),%on recup les donnée Orp= orientation ancienne
										decal_element(Arrivee,Orientation, L, Type, Place),!.%on deplace

orientation_valide(Coup):- Coup=(_,_,n),!.
orientation_valide(Coup):- Coup=(_,_,s),!.
orientation_valide(Coup):- Coup=(_,_,e),!.
orientation_valide(Coup):- Coup=(_,_,w),!.
orientation_valide(Coup):- Coup=(_,0,0),!.

coup_valide(Coup):-Coup=(Depart,Arrivee,Orientation),%si on change juste l'orientation
						orientation_valide(Coup),
						Depart==Arrivee,
						plateau(Plat),
						type_case(Depart, Plat, _, _, _, OrP),
						Orientation \= OrP ,!.

coup_valide(Coup):-Coup=(Depart,Arrivee,_),%si on sort une piece
						orientation_valide(Coup),
						Arrivee==0,
						plateau(Plat),
						type_case(Depart, Plat, _, _, _, _),
						Depart\=Arrivee,						
						bord(Depart),!.
						
coup_valide(Coup):-Coup=(Depart,Arrivee,_),%si on rentre une piece
						Depart==0,
						\+ place_disponible(Arrivee),
						orientation_valide(Coup),					
						bord(Arrivee),!.
						
coup_valide(Coup):-Coup=(Depart,Arrivee,Orientation),%si on rentre une piece avec pousse
						Depart==0,
						place_disponible(Arrivee),
						orientation_valide(Coup),					
						bord(Arrivee),
						poussee_possible(Arrivee, Orientation),!.
						
coup_valide(Coup):-	Coup=(_,Arrivee,_),%cas avec case libre
					orientation_valide(Coup),
					\+ place_disponible(Arrivee),
					a_cote(Coup),!.%case libre a coté
					
coup_valide(Coup) :- Coup=(Depart,Arrivee,Orientation),
					a_cote(Coup),
					plateau(Plat),
					type_case(Depart, Plat, _, _, _, OrP),
					OrP == Orientation,
					place_disponible(Arrivee),
					poussee_possible(Depart, OrP).

						
%a_cote(Coup):-	Coup=(Depart,Arrivee,Orientation),plateau(Plat), D is Depart - Arrivee, D == -1.
%a_cote(Coup):-	Coup=(Depart,Arrivee,Orientation),plateau(Plat),D is Depart - Arrivee, D == 1.
%a_cote(Coup):-	Coup=(Depart,Arrivee,Orientation),plateau(Plat), D is Depart - Arrivee, D == -10.
%a_cote(Coup):-	Coup=(Depart,Arrivee,Orientation),plateau(Plat), D is Depart - Arrivee, D ==10.						
a_cote(Coup):-Coup=(Depart,Arrivee,_),caseAdjacente(Depart,Arrivee).
caseAdjacente(Depart,Arrivee):- Diff is Depart - Arrivee, element(Diff,[-1,1,-10,10]), !.

% test si la case donné en argument est déjé utilisé sur le plateau.
% renvoie T si la case n est pas disponible
place_disponible(NumCase):- plateau(P),nth0(0, P, X),element((NumCase, _), X),!. 
place_disponible(NumCase):- plateau(P),nth0(1, P, X),element((NumCase, _), X),!.
place_disponible(NumCase):- plateau(P),nth0(2, P, X),element(NumCase, X).

initplateau:-plateau_depart(T), set_plateau(T), affiche_plateau(T), set_player(e).


jouer:- initplateau,set_player(J), tour(J,0).  
tour(e, 0):- nl, plateau(P), affiche_plateau(P),joueur1_lecture(P), vainqueur(V), tour(r,V).
tour(r, 0):- nl, plateau(P), affiche_plateau(P),joueur2_lecture(P), vainqueur(V), tour(e,V).
tour(_, e):- nl, write('Le jour 1 gagne !').
tour(_, r):- nl, write('Le jour 2 gagne !').


joueur1_lecture(Plateau):-write('Joueur1: entrez coup (D,A,S)'), nl,read(X), coup_valide(X), joueur1_jeu(X,Plateau), set_player(r),!.
joueur1_lecture(Plateau):- joueur1_lecture(Plateau).
joueur1_jeu(Coup,_):- inserer_P(Coup, e),!. %test si insertion de piéce
joueur1_jeu(Coup,Plateau):- deplacer_piece(Coup,Plateau),!.
joueur1_jeu(Coup,_):- Coup=(Depart,_,Orientation), decal_ligne(Depart,Orientation),!.

joueur2_lecture(Plateau):-write('Joueur2: entrez coup (D,A,S)'), nl,read(X), coup_valide(X), joueur2_jeu(X,Plateau), set_player(e),!.
joueur2_lecture(Plateau):- joueur2_lecture(Plateau).
joueur2_jeu(Coup,_):- inserer_P(Coup,r),!.%test si insertion de piéce
joueur2_jeu(Coup,Plateau):- deplacer_piece(Coup,Plateau),!.
joueur2_jeu(Coup,_):- Coup=(Depart,_,Orientation), decal_ligne(Depart,Orientation),!.

% Lance automatiquement jouer (ou initplateau) à l inclusion du fichier
%:-initialization(jouer). 
:-initialization(initplateau).

% IA : findall, setof, bagof
% findall((X,Y), (member(X, [0,1,2,3]), Y is X*X, Y>3) L). % renvoie tous les carrés de 0,1,2,3 qui sont >3: L=[(2,4),(3,9)]
% minimax: on développe tous les coups possibles et on va vers la meilleure issue pour nous
% Il faut une valeur permettant de calculer si un jeu est favorable ou non (max si éléphants, min si rhino)

% Prédicats : plateau_gagnant, plateau_perdant

% faire prédicat deplacer piece en utilisant place disponible. Gérer les pieces a retirer.